package com.yingjie.yingjieorder.common;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.rabbitmq.client.Channel;
import com.yingjie.yingjieorder.service.OrderService;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.core.ReturnedMessage;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;

import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Component
public class OrderRabbitMQUtil {
    @Resource
    private RabbitTemplate rabbitTemplate;
    @Resource
    private ObjectMapper objectMapper;

    @Resource
    private OrderService orderService;
    //bean初始化后执行
    @PostConstruct
    private void init(){
        //是否成功发送到交换机 的回调
        //使用rabbitTemplate.setConfirmCallback设置回调函数，当消息发送到exchange后回调confirm方法。在方法中判断ack，如果为true，则发送成功，如果为false，则发送失败，需要处理；
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            @Override
            // 发送消息携带的数据：correlationData 是否成功：b   失败理由：s
            public void confirm(CorrelationData correlationData, boolean b, String s) {
                if(!b){
                    System.out.println("发送到交换机失败理由："+s);
                }
            }
        });

        //交换机是否发送到队列的回调
        //使用rabbitTemplate.setReturnCallback设置退回函数，当消息从exchange路由到
        //queue失败后，则会将消息退回给producer，并执行回调函数returnedMessage；
        rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {
            @Override
            public void returnedMessage(ReturnedMessage returnedMessage) {
//                System.out.println("发送到队列失败"+returnedMessage.getMessage());
            }
        });

    }
    //发送普通消息
    public void sendOrderMsg(Map o) throws JsonProcessingException {
        String os = objectMapper.writeValueAsString(o);
        Message message = MessageBuilder.withBody(os.getBytes()).build();
        rabbitTemplate.convertAndSend(RabiitMqConst.EXCHANGE, RabiitMqConst.ORDER_QUEUE,message);

    }

    //接受普通消息
    @RabbitListener(queues = {RabiitMqConst.ORDER_QUEUE},concurrency = "8")
    public void receiveOrderMsg(Message message, Channel channel){
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {

            Map map = objectMapper.readValue(message.getBody(), Map.class);


            //数据库插入订单

            //手动确认接受到mq消息
            channel.basicAck(deliveryTag,false);
        } catch (IOException e) {
            System.out.println(e.getMessage());
            //订单处理异常
            try {
                //订单获取失败，消息回到队列
                System.out.println("处理消息错误，订单号为："+deliveryTag);
                channel.basicNack(deliveryTag,false,true);
            } catch (IOException ex) {
                throw new RuntimeException(ex);
            }
            throw new RuntimeException(e);
        }
    }


    //发送延迟消息
    public void sendDelayOrderMsg(Integer orderId,Long delay) throws JsonProcessingException {
        MessageProperties messageProperties =new MessageProperties();
        messageProperties.setHeader("x-delay",delay); //延迟队列必须设置这个消息头
        Message delayMessage= MessageBuilder.withBody(String.valueOf(orderId).getBytes())
                .andProperties(messageProperties)
                .build();

        rabbitTemplate.convertAndSend(RabiitMqConst.DELAY_EXCHANGE,RabiitMqConst.DELAY_ORDER_QUEUE,delayMessage);
    }

    //接受演示消息
    @RabbitListener(queues = {RabiitMqConst.DELAY_ORDER_QUEUE},concurrency = "8")
    public void receiveDelayOrderMsg(Message message, Channel channel){
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        //TODO 获取消息体，是订单id
        String strOrderId=new String(message.getBody());
        Integer orderId=Integer.valueOf(strOrderId);
        try {
            orderService.finishOrder(orderId);
            channel.basicAck(deliveryTag,false);
        } catch (IOException e) {
            System.out.println(e.getMessage());
            System.out.println("处理延迟消息错误，单号为："+deliveryTag);
            try {
                channel.basicNack(deliveryTag,false,true);
            } catch (IOException ex) {
                throw new RuntimeException(ex);
            }
            throw new RuntimeException(e);
        }
    }
}
